\section{Scanning for uses of identifiers} \subsection{Main program} <<*>>= #include #include #include #include #include "errors.h" #include "match.h" #include "getline.h" #include "recognize.h" These choices of alphanumerics and symbols seem to work for most languages. Making [[@]] alphanumeric helps {\LaTeX}, and making [[#]] alphanumeric helps avoid false hits on C preprocessor directives like [[#define]] and [[#include]]. <<*>>= static Recognizer nwindex; #define ALPHANUM "_'@ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#" #define SYMBOLS "!%^&*-+:=|~<>./?`" /* note $ and \ both delimiters */ @ %def ALPHANUM SYMBOLS By default, find uses within quoted code ([[[[...]]]]). <<*>>= static int showquotes = 1; <> <> <<*>>= main(int argc, char **argv) { FILE *fp; char *myself = *argv; int i; for (i = 1; i < argc && argv[i][0] == '-' && argv[i][1] != 0; i++) if (!strcmp(argv[i], "-noquote")) showquotes = 0; else errormsg(Error, "%s: unknown option -%c\n", myself, argv[i][1]); nwindex = new_recognizer(ALPHANUM, SYMBOLS); if (i == argc) { <> } else { <> stop_adding(nwindex); add_use_markers(stdin, stdout); } exit(errorlevel); return errorlevel; /* slay warning */ <>= for (; i < argc; i++) if ((fp=fopen(argv[i],"r"))==NULL) errormsg(Error, "%s: couldn't open file %s\n", myself, argv[i]); else { read_ids(fp); fclose(fp); } <>= static void read_ids(FILE *in); <<*>>= static void read_ids(FILE *in) { char *line; while ((line = getline(in)) != NULL) { if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0; add_ident(nwindex, line); } @ %def read_ids <>= { FILE *tmp = tmpfile(); char *line; if (tmp == NULL) <> while ((line = getline(stdin)) != NULL) { if (fputs(line, tmp) == EOF) <> if (is_index(line, "defn")) { if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0; add_ident(nwindex, line+1+5+1+4+1); } else if (is_index(line, "localdefn")) { if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = 0; add_ident(nwindex, line+1+5+1+9+1); } } rewind(tmp); stop_adding(nwindex); add_use_markers(tmp, stdout); <>= typedef struct line_and_outfile { char *line; FILE *out; } LineOut; <>= static void add_use_markers(FILE *in, FILE *out); <<*>>= static void add_use_markers(FILE *in, FILE *out) { char *line; int incode = 0; LineOut info; info.line = (char *)0; info.out = out; while ((line = getline(in)) != NULL) { if (is_begin(line, "code") || showquotes && is_keyword(line, "quote")) incode = 1; else if (is_end(line, "code") || is_keyword(line, "endquote")) incode = 0; if (is_keyword(line, "text") && incode) { info.line = line + 6; /* skip "@text " */ search_for_ident(nwindex, line, write_index_use, &info); if (*info.line && *info.line != '\n') fprintf(out, "@text %s", info.line); /* has newline */ } else fprintf(out, "%s", line); } @ %def add_use_markers We gradually cut out the uses, and the tail of the line is left in [[info.line]], to be printed by the code above. There's a tricky bug lurking here---if one identifier is a prefix of another, but both are recognized (as with the C$++$ [[::]] separator), we have to avoid writing them both out in full, because that would duplicate text unnecessarily. As a result, we always emit the line in pieces. The function [[emit_up_to(f, s, limit)]] emits the piece of the string [[s]] up to but not including [[limit]], if any. It returns [[limit]] or [[s]], whichever is greater. <<*>>= static void write_index_use(void *closure, char *id, char *instance) { LineOut *info = (LineOut *) closure; info->line = emit_up_to(info->out, info->line, instance); fprintf(info->out, "@index use %s\n", id); info->line = emit_up_to(info->out, info->line, instance + strlen(id)); @ %def write_index_use <<*>>= static char *emit_up_to(FILE *f, char *s, char *limit) { if (s < limit) { char saved = *limit; *limit = 0; fprintf(f, "@text %s\n", s); *limit = saved; return limit; } else { return s; <>= static void write_index_use(void *closure, char *id, char *instance); static char *emit_up_to(FILE *f, char *s, char *limit); <>= errormsg(Fatal, "%s: couldn't open temporary file\n"); <>= errormsg(Fatal, "%s: error writing temporary file\n");